/*
 * Copyright 2006 Brian Watkins
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */


package bw.weather;

import java.net.*;
import java.io.*;
import java.util.regex.*;
import javax.xml.parsers.*;
import bw.util.*;
import bw.net.*;
import bw.refresh.*;

public class WeatherLocation extends RefreshingDataSource
{
    static final long serialVersionUID = 1611252279809562334L;

    private static String WEATHER_URI = "http://xoap.weather.com/weather/local/";
    private static String LOCATION_URI = "http://xoap.weather.com/search/search?where=";
    private static String CURRENT_CONDITIONS_PARAM = "cc";
    private static String FORECAST_PARAM = "dayf";
    private static String LINK_PARAM = "link=xoap";
    private static String PROD_PARAM = "prod=xoap";
    private static String PARTNER_PARAM = "par";
    private static String LICENSE_PARAM = "key";
    private static String WEATHER_LOC_KEY = "WD-WEATHER-LOC_";

    private String _partnerId = null;
    private String _licenseKey = null;
    private String _locationId = null;
    private String _locationName = null;
    private int _forecastLength = 0;
    private WeatherReport _report = null;
    //private Pattern _zipcode = null;
    //private Pattern _citycode = null;

    //Note: WeatherLocation encapsulates all the logic for requesting a weather
    //      report for a particular location. This way if we need to change
    //      from weather.com you just change this class
    //      or make it an interface and provide various implementations

    public static String createWeatherSrc ( String partnerId, String licenseKey, 
                                            String locId, int length )
    {
        String url = WEATHER_URI + Util.URLEncode( locId ) + "?" +
            CURRENT_CONDITIONS_PARAM + "=*&" + 
            FORECAST_PARAM + "=" + String.valueOf( length ) + "&" +
            LINK_PARAM + "&" + 
            PROD_PARAM + "&" +
            PARTNER_PARAM + "=" + partnerId + "&" +
            LICENSE_PARAM + "=" + licenseKey;

        //Log.getInstance().write( url );

        return url;
    }

    public static String getOSKey ( String id )
    {
        return WEATHER_LOC_KEY + id;
    }

    public WeatherLocation ( String partnerId, String licenseKey, int length, 
                             int updateInterval  )
    {
        //updateInterval is in minutes
        super( null, null, updateInterval * 60 * 1000L );

        _partnerId = partnerId;
        _licenseKey = licenseKey;
        _forecastLength = length;

        //compile the regexps for zipcode and citycode
        //_zipcode = Pattern.compile( "^\\d{5}$" );
        //_citycode = Pattern.compile( "^[A-Z]{4}\\d{4}$" );
    }

    public String getLocationId () { return _locationId; }

    public void setLocationId ( String locId ) throws InvalidWeatherLocationException 
    { 
        String url = LOCATION_URI + Util.URLEncode( locId );
        WeatherLocXmlHandler handler = new WeatherLocXmlHandler();

        try 
        {
            NetResource resource = new NetResource( url );
            SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
            parser.parse( resource.getInputStream(), handler );
        }
        catch ( Exception ex ) 
        {
            Log.getInstance().write( "Problem getting location (" + locId + 
                                     ") for weather lookup: " + url, ex );
        }

        if ( handler.hasLocationId() )
        {
            _locationId = handler.getLocationId();
            _locationName = handler.getLocationName();
        }
        else
        {
            _locationId = locId;
            InvalidWeatherLocationException ex = new InvalidWeatherLocationException( locId, 
                                                                                      "Invalid weather location" );
            throw ex;
        }

        //update the id in RefreshingDataSource
        setId( getOSKey( _locationId ) );

        //have to reset the src to reflect the new locationId
        setSrc( WeatherLocation.createWeatherSrc( _partnerId, _licenseKey, 
                                                  _locationId, _forecastLength ) );
    }

    public int getForecastLength () { return _forecastLength; }

    public WeatherReport getReport () { return _report; }

    public void setReport ( WeatherReport wr ) { _report = wr; }

    public String getLocationName ()
    {
        return ( _locationName == null ? _locationId : _locationName );
    }

    public void refreshComplete ()
    {
        sync();
    }

    public void updateSource ( HttpURLConnection conn ) throws RefresherException
    {
        Log.getInstance().write( "Updating weather: " + getLocationId() );

        try 
        {
            WeatherXmlHandler handler = new WeatherXmlHandler();
            SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
            InputStream stream = conn.getInputStream();
            parser.parse( stream, handler );
            stream.close();
            _report = handler.getReport();
        }
        catch ( Exception ex ) 
        {
            RefresherException rex = new RefresherException( "Problem parsing weather xml", ex );
            rex.setRecoverable( false );
            throw rex;
        }
    }

    public boolean equals ( Object obj )
    {
        boolean equals = false;
        if ( obj != null )
        {
            equals = this.getLocationId().equals( ( (WeatherLocation) obj ).getLocationId() );
        }
        return equals;
    }

    public String toXML ()
    {
        StringBuffer buf = new StringBuffer();
        buf.append( "<location id=\"" );
        buf.append( getLocationId() );
        buf.append( "\" name=\"" );
        buf.append( getLocationName() );
        buf.append( "\" />" );

        return buf.toString();
    }
}